package clientN

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"wxp2p/common"

	"github.com/pion/webrtc/v3"
)

type WebrtcOffer struct {
	WebrtcId             int32
	DataSendChan         chan []byte
	DataRecvChan         chan []byte
	PeerConnection       *webrtc.PeerConnection
	PendingCandidatesMux *sync.Mutex
	PendingCandidates    []*webrtc.ICECandidate
	SignalClient         *ClientClient
	tcpClient            *TCPClient
	hpPeer               *common.HostPortPeer
}

func NewWebrtcOffer(id int32, hp *common.HostPortPeer) *WebrtcOffer {
	wo := WebrtcOffer{hpPeer: hp, WebrtcId: id}
	wo.DataRecvChan = make(chan []byte)
	wo.DataSendChan = make(chan []byte)
	return &wo
}

func (wa *WebrtcOffer) Run() {
	go wa.DataRecvLoop()
	go wa.WebrtcLoop()
}

func (wa *WebrtcOffer) SignalHandler(sig common.P2PSignal) {
	switch sig.SigType {
	case common.ECandidate:
		if candidateErr := wa.PeerConnection.AddICECandidate(webrtc.ICECandidateInit{Candidate: string(sig.Content)}); candidateErr != nil {
			common.LogDebug(candidateErr)
		}
	case common.ESdp:
		sdp := webrtc.SessionDescription{}
		json.Unmarshal([]byte(sig.Content), &sdp)
		if sdpErr := wa.PeerConnection.SetRemoteDescription(sdp); sdpErr != nil {
			common.LogDebug(sdpErr)
		}
		wa.PendingCandidatesMux.Lock()
		for _, c := range wa.PendingCandidates {
			p2psig := common.P2PSignal{WebrtcId: wa.WebrtcId, SigType: common.ECandidate, Content: c.ToJSON().Candidate}
			wa.SignalClient.SendChan <- p2psig
		}
		wa.PendingCandidatesMux.Unlock()
	}
}

// client-client
func (wa *WebrtcOffer) WebrtcLoop() {
	wa.PendingCandidatesMux = &sync.Mutex{}
	wa.PendingCandidates = make([]*webrtc.ICECandidate, 0)
	done := make(chan struct{})
	// Create a new RTCPeerConnection
	peerConnection, err := webrtc.NewPeerConnection(common.WebrtcConfig)
	if err != nil {
		common.LogDebug(err)
	}
	wa.PeerConnection = peerConnection
	defer func() {
		if cErr := wa.PeerConnection.Close(); cErr != nil {
			fmt.Printf("cannot close peerConnection: %v\n", cErr)
		}
		close(wa.DataSendChan)
		close(wa.DataRecvChan)
		wa.tcpClient.StopListen()
		delete(wa.SignalClient.WebrtcArray, wa.WebrtcId)
	}()

	// When an ICE candidate is available send to the other Pion instance
	// the other Pion instance will add this candidate by calling AddICECandidate
	wa.PeerConnection.OnICECandidate(func(c *webrtc.ICECandidate) {
		if c == nil {
			return
		}

		wa.PendingCandidatesMux.Lock()
		defer wa.PendingCandidatesMux.Unlock()
		desc := wa.PeerConnection.RemoteDescription()
		if desc == nil {
			wa.PendingCandidates = append(wa.PendingCandidates, c)
		} else {
			sig := common.P2PSignal{WebrtcId: wa.WebrtcId, SigType: common.ECandidate, Content: c.ToJSON().Candidate}
			wa.SignalClient.SendChan <- sig
		}
	})

	// Create a datachannel with label 'data'
	dataChannel, err := wa.PeerConnection.CreateDataChannel("data", nil)
	if err != nil {
		panic(err)
	}

	// Set the handler for Peer connection state
	// This will notify you when the peer has connected/disconnected
	wa.PeerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
		log.Printf("p2p连接状态改变: %s\n", s.String())
		if s == webrtc.PeerConnectionStateConnected {
			log.Println("p2p连接成功")
		}
		if s == webrtc.PeerConnectionStateFailed || s == webrtc.PeerConnectionStateDisconnected {
			// Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart.
			// Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout.
			// Note that the PeerConnection may come back from PeerConnectionStateDisconnected.
			log.Println("p2p连接失败正在退出")
			close(done)
		}
	})

	// Register channel opening handling
	dataChannel.OnOpen(func() {
		log.Println("p2p数据通道准备就绪")
		wa.SignalClient.callbackChan <- "p2p数据通道准备就绪"
		wa.tcpClient = NewTCPClient(wa.hpPeer.LocalHost, wa.hpPeer.LocalPort, wa)
		go wa.tcpClient.ListenLocalTCP()
		for buf := range wa.DataSendChan {
			sendErr := dataChannel.Send(buf)
			if sendErr != nil {
				common.LogDebug(sendErr)
			}
		}
	})

	// Register text message handling
	dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
		wa.DataRecvChan <- msg.Data
	})

	// Create an offer to send to the other process
	offer, err := wa.PeerConnection.CreateOffer(nil)
	if err != nil {
		panic(err)
	}

	// Sets the LocalDescription, and starts our UDP listeners
	// Note: this will start the gathering of ICE candidates
	if err = wa.PeerConnection.SetLocalDescription(offer); err != nil {
		panic(err)
	}

	// Send our offer to the HTTP server listening in the other process
	payload, err := json.Marshal(offer)
	if err != nil {
		panic(err)
	}
	p2psig := common.P2PSignal{WebrtcId: wa.WebrtcId, SigType: common.ESdp, Content: string(payload)}
	wa.SignalClient.SendChan <- p2psig
	// Block forever
	<-done
}

func (wa *WebrtcOffer) DataRecvLoop() {
	for buf := range wa.DataRecvChan {
		if wa.tcpClient != nil {
			wa.tcpClient.RecDataHandler(buf)
		}
	}
	common.LogDebug("退出webrtc datarecvloop")
}
